package annotation

import (
	"go/ast"
)

// SingleFileEntryVisitor 这部分和课堂演示差不多，但是我建议你们自己试着写一些
type SingleFileEntryVisitor struct {
	file *fileVisitor
}

func (s *SingleFileEntryVisitor) Get() File {
	fi := File{}
	fi.Node = s.file.ans.Node
	fi.Annotations = s.file.ans
	fi.Types = make([]Type, len(s.file.types))

	for i, tv := range s.file.types {
		fi.Types[i] = tv.Get()
	}

	return fi
}

func (s *SingleFileEntryVisitor) Visit(node ast.Node) ast.Visitor {
	fi := &fileVisitor{}
	fi.Visit(node)
	s.file = fi
	return s.file
}

type fileVisitor struct {
	ans     Annotations[*ast.File]
	types   []*typeVisitor
	visited bool
}

func (f *fileVisitor) Get() File {
	fi := File{}
	fi.Node = f.ans.Node
	fi.Annotations = f.ans
	fi.Types = make([]Type, len(f.types))

	for i, tv := range f.types {
		fi.Types[i] = tv.Get()
	}

	return fi
}

func (f *fileVisitor) Visit(node ast.Node) ast.Visitor {
	nf, ok := node.(*ast.File)
	if !ok {
		return nil
	}

	f.visited = true
	f.ans = newAnnotations(nf, nf.Doc)

	for _, decl := range nf.Decls {
		gd, ok := decl.(*ast.GenDecl)
		if !ok {
			continue
		}

		for _, spec := range gd.Specs {
			ts, ok := spec.(*ast.TypeSpec)
			if !ok {
				continue
			}

			if ts.Doc == nil {
				continue
			}

			tv := &typeVisitor{}
			tv.ans = newAnnotations(ts, ts.Doc)
			f.types = append(f.types, tv)
			tv.Visit(ts)
		}
	}

	return f
}

type File struct {
	Annotations[*ast.File]
	Types []Type
}

type typeVisitor struct {
	ans    Annotations[*ast.TypeSpec]
	fields []Field
}

func (t *typeVisitor) Get() Type {
	ti := Type{}
	ti.Annotations = t.ans
	ti.Fields = t.fields
	return ti
}

func (t *typeVisitor) Visit(node ast.Node) (w ast.Visitor) {

	switch node.(type) {
	case *ast.TypeSpec:
		ts := node.(*ast.TypeSpec)
		switch ts.Type.(type) {
		case *ast.InterfaceType:
			for _, field := range ts.Type.(*ast.InterfaceType).Methods.List {
				f := Field{}
				f.Annotations = newAnnotations(field, field.Doc)
				t.fields = append(t.fields, f)
			}
		case *ast.StructType:
			for _, field := range ts.Type.(*ast.StructType).Fields.List {
				f := Field{}
				f.Annotations = newAnnotations(field, field.Doc)
				t.fields = append(t.fields, f)
			}
		case *ast.FuncType:

		case *ast.Ident:

		default:
			return nil
		}
	}

	return t
}

type Type struct {
	Annotations[*ast.TypeSpec]
	Fields []Field
}

type Field struct {
	Annotations[*ast.Field]
}
